#!/usr/bin/perl
use strict;
use File::Basename;
use Cwd 'abs_path';

my $bindir;
my $sqlite;

BEGIN {
    $bindir = abs_path(dirname($0));
    $ENV{PATH} = ':/opt/CollabNet_Subversion/bin'
               . ':/sbin:/usr/sbin:/bin:/usr/local/sbin'
               . ':/usr/local/bin:/usr/bin:/volume/labtools/bin'
               . ':/volume/buildtools/bin:/volume/sw-tools/bin'
               . ':/usr/ccs/bin'
               ;
    if (-e '/usr/local/bin/sqlite3') {
        $sqlite = '/usr/local/bin/sqlite3';
    }
    elsif (-e '/usr/bin/sqlite3') {
        $sqlite = '/usr/bin/sqlite3';
    }
    elsif (-e "$bindir/sqlite3-freebsd-captive/sqlite") {
        $sqlite = "$bindir/sqlite3-freebsd-captive/sqlite";
    }
}

package JNPR::Utils;


my $g_sql_out = [];
my $S;

sub set_url
{
    ($S) = @_;
}


#
# --------------------------------------------------------------------
# --------------------------------------------------------------------
{
my $branch_of_rev_cache;
sub get_branch_of_rev
{
    my ($db, $rev) = @_;

    $branch_of_rev_cache = {} unless (defined $branch_of_rev_cache);
    return $branch_of_rev_cache->{$rev}
        if (exists $branch_of_rev_cache->{$rev});
    
    my $cmd = "select branch from nodes where rev = $rev";
    sql_query($db, \$cmd, $g_sql_out);
    my $branch = $g_sql_out->[0];
    chomp $branch;

    $branch_of_rev_cache->{$rev} = $branch;
    return $branch;
}
}

#
# --------------------------------------------------------------------
# --------------------------------------------------------------------
sub branch_full_name
{
    my ($db, $branch, $rev) = @_;
    return 'trunk' if ($branch eq 'trunk');

    my $cmd = "select max(creation_rev) from branches where branch_name = \"$branch\" and creation_rev <= $rev;\n";
    sql_query($db, \$cmd, $g_sql_out);
    my $creation_rev = $g_sql_out->[0];
    chomp $creation_rev;
    return "$branch\@$creation_rev";
}

#
# --------------------------------------------------------------------
# --------------------------------------------------------------------
sub get_real_rev
{
    my ($db, $branch, $rev) = @_;
    my $cmd = "SELECT max(rev) FROM nodes WHERE branch=\"$branch\" AND rev <= $rev;\n";
    sql_query($db, \$cmd, $g_sql_out);
    my $foundrev = $g_sql_out->[0];
    chomp $foundrev;
    if ($foundrev eq '') { # Can't find it in db. Ask svn
        print "Warning: fetching REAL $branch\@$rev Rev from svn\n";
        $cmd = "svn info $S/branches/$branch\@$rev | grep 'Last Changed Rev:'";
        if ($branch eq 'trunk') {
            $cmd = "svn info $S/trunk\@$rev | grep 'Last Changed Rev:'";
        }
        my $result = `$cmd`;
        chomp $result;
        ($foundrev) = $result =~ /(\d+)$/;
    }
    return $foundrev;
}

#
# --------------------------------------------------------------------
# --------------------------------------------------------------------
{
my $n;
sub sql_query
{
    my ($db, $ref_query_string, $aref_sql_out) = @_;
    $$ref_query_string =~ s{[\s;]*$}{;\n}s;

    unless (defined $n) {
        $n = 0;
        system "mkdir -p debug";
    }
    $n++;
    ($n) = "000$n" =~ /(...)$/;
    open my $f, ">>debug/q-$$-$n.txt" or die;
    print $f $$ref_query_string;
    close $f;
    my $tmpfile = make_tmp_file_name();
    my $f;
    open $f, ">$tmpfile" or die "Error: Cannot write to $tmpfile\n";
    print $f $$ref_query_string;
    close $f;
    my $cmd = "$sqlite $db <$tmpfile";
    @$aref_sql_out = `$cmd`;
    my $status = $?;
#smandal
    if ($status) {
        print <<"EOF";
---------------------------------------------------
CMD: $$ref_query_string
STATUS: $status
---------------------------------------------------
EOF
    }
    else {
        unlink $tmpfile;
    }
#smandal
    return $status;
}
}

#
# --------------------------------------------------------------------
# --------------------------------------------------------------------
sub make_tmp_file_name
{
    my $f0 = '/tmp/tmpfile-' . time;
    my $f = $f0;
    my $n = 0;
    while (-e $f) {
        $n++;
        $f = "$f0.$n";
    }
    return $f;
}

#
# --------------------------------------------------------------------
# --------------------------------------------------------------------
sub scan_rlog_for_merge_or_branch
{
    my ($aref_rlog) = shift;
    my $rlog = grok_rlog($aref_rlog);

    my $r           = $rlog->{rev};
    my $branch      = $rlog->{branch};
    my $dead_branch = $rlog->{dead_branch};

    if ($r eq '') {
        return 1;
    }
    my ($d) = "00$r" =~ /(..)$/;
    if (-e "$d/$r-mergeinfo.txt") {
        unlink "$d/$r-mergeinfo.txt";
    }
    if (-e "$d/$r-mergeinfo--.txt") {
        unlink "$d/$r-mergeinfo--.txt";
    }
    if (-e "$d/$r-branched.txt") {
        unlink "$d/$r-branched.txt";
    }

    if ($rlog->{parent_branch}) {
        open F, ">$d/${r}-${branch}-branch-created.txt";
        close F;
        open F, ">$d/${r}-branched.txt";
        close F;
    }
    if ($rlog->{dead_branch}) {
        open F, ">$d/${r}-${dead_branch}-branch-deleted.txt";
        close F;
        open F, ">$d/${r}-branch-deleted.txt";
        close F;
    }
    if ($rlog->{merged}) {
        open F, ">$d/${r}-${branch}-merged.txt";
        close F;
        open F, ">$d/${r}-merged.txt";
        close F;
        {
            my $path = "branches/$branch";
            if ($branch eq 'trunk') {
                $path = "trunk";
            }
            my $cmd;
            $cmd = "svn pg svn:mergeinfo $S/$path\@$r >$d/${r}-mergeinfo.txt";
            system $cmd;
            my $revminusone = $r - 1;
            $cmd = "svn pg svn:mergeinfo $S/$path\@$revminusone >$d/${r}-mergeinfo--.txt";
            system $cmd;
        }
    }

}

#
# --------------------------------------------------------------------
# Returns reference to this hash
#   {
#       rev
#       author
#       date
#       branch
#       parent_branch
#       parent_rev
#       dead_branch
#       merged
#   }
# --------------------------------------------------------------------
sub grok_rlog
{
    my ($aref_rlog) = shift;

=head looks
1.a 0  ------------------------------------------------------------------------
    1  r385714 | builder | 2010-06-21 23:28:23 -0700 (Mon, 21 Jun 2010) | 34 lines
    2  Changed paths:
    3     A /branches/IB4_11_1_BRANCH (from /branches/IB4_10_5_BRANCH:385713)

1.b 0  ------------------------------------------------------------------------
    1  r385714 | builder | 2010-06-21 23:28:23 -0700 (Mon, 21 Jun 2010) | 34 lines
    2  Changed paths:
    3     A /branches/IB4_11_1_BRANCH (from /trunk:385713)

2.  0  ------------------------------------------------------------------------
    1  r385714 | builder | 2010-06-21 23:28:23 -0700 (Mon, 21 Jun 2010) | 34 lines
    2  Changed paths:
    3     A /branches/IB4_11_1_BRANCH (from /branches/IB4_10_5_BRANCH:385713)
    4     D /branches/IB4_10_5_BRANCH

3.  0  ------------------------------------------------------------------------
    1  r385714 | builder | 2010-06-21 23:28:23 -0700 (Mon, 21 Jun 2010) | 34 lines
    2  Changed paths:
    3     D /branches/IB4_10_5_BRANCH

4.a 0  ------------------------------------------------------------------------
    1  r385714 | builder | 2010-06-21 23:28:23 -0700 (Mon, 21 Jun 2010) | 34 lines
    2  Changed paths:
    3     D /branches/IB4_10_5_BRANCH
    4     A /branches/IB4_11_1_BRANCH (from /branches/IB4_10_5_BRANCH:385713)

4.b 0  ------------------------------------------------------------------------
    1  r385714 | builder | 2010-06-21 23:28:23 -0700 (Mon, 21 Jun 2010) | 34 lines
    2  Changed paths:
    3     D /branches/IB4_10_5_BRANCH
    4     A /branches/IB4_11_1_BRANCH (from /trunk:385713)

5.a 0  ------------------------------------------------------------------------
    1  r385714 | builder | 2010-06-21 23:28:23 -0700 (Mon, 21 Jun 2010) | 34 lines
    2  Changed paths:
    3     M /branches/IB4_11_1_BRANCH

5.b 0  ------------------------------------------------------------------------
    1  r385714 | builder | 2010-06-21 23:28:23 -0700 (Mon, 21 Jun 2010) | 34 lines
    2  Changed paths:
    3     M /branches/IB4_11_1_BRANCH

6.a 0  ------------------------------------------------------------------------
    1  r385714 | builder | 2010-06-21 23:28:23 -0700 (Mon, 21 Jun 2010) | 34 lines
    2  Changed paths:
    3     M /branches/IB4_11_1_BRANCH/a/b/c

6.b 0  ------------------------------------------------------------------------
    1  r385714 | builder | 2010-06-21 23:28:23 -0700 (Mon, 21 Jun 2010) | 34 lines
    2  Changed paths:
    3     M /trunk/a/b/c

=cut

    my $returnhash = {
                       'rev'           => '',
                       'author'        => '',
                       'date'          => '',
                       'branch'        => '',
                       'parent_branch' => '',
                       'parent_rev'    => '',
                       'dead_branch'   => '',
                       'merged'        => '',
                     };

    my ($r, $author, $date);

    if ($aref_rlog->[1] =~ /^r(\d+?)\s*\|\s*([^\|]+?)\s*\|\s*(.+?) \(/) {
        $r = $1;
        $author = $2;
        $date = $3;
    }
    else {
        return $returnhash;
    }

    my $branch          = '';
    my $parent_branch   = '';
    my $parent_rev      = '';
    my $dead_branch     = '';
    my $merged          = 0;
    # Case 1,2
    if ($aref_rlog->[3] =~ m{^...A /branches/([^/]+) \(from /branches/([^/]+):(\d+)\)\n?$}s) {
        $branch = $1;
        $parent_branch = $2;
        $parent_rev = $3;
        if ($aref_rlog->[4] =~ m{^...D /branches/([^/]+?)\n?$}s) {
            $dead_branch = $1;
        }
    }
    elsif ($aref_rlog->[3] =~ m{^...A /branches/([^/]+) \(from /trunk:(\d+)\)\n?$}s) {
        $branch = $1;
        $parent_branch = 'trunk';
        $parent_rev = $2;
        if ($aref_rlog->[4] =~ m{^...D /branches/([^/]+?)\n?$}s) {
            $dead_branch = $1;
        }
    }
    # Case 3,4
    elsif ($aref_rlog->[3] =~ m{^...D /branches/([^/]+?)\n?$}s) {
        $dead_branch = $1;
        if ($aref_rlog->[4] =~ m{^...A /branches/([^/]+) \(from /branches/([^/]+):(\d+)\)\n?$}s) {
            $branch = $1;
            $parent_branch = $2;
            $parent_rev = $3;
        }
        elsif ($aref_rlog->[4] =~ m{^...A /branches/([^/]+) \(from /trunk:(\d+)\)\n?$}s) {
            $branch = $1;
            $parent_branch = 'trunk';
            $parent_rev = $3;
        }
    }
    # Case 5
    elsif ($aref_rlog->[3] =~ m{^...M /branches/([^/]+?)\n?$}s) {
        $branch = $1;
        $merged = 1;
    }
    elsif ($aref_rlog->[3] =~ m{^...M /trunk\n?$}s) {
        $branch = 'trunk';
        $merged = 1;
    }
    # Case 6
    elsif ($aref_rlog->[3] =~ m{^.... /branches/([^/]+)/}) {
        $branch = $1;
    }
    elsif ($aref_rlog->[3] =~ m{^.... /trunk/}) {
        $branch = 'trunk';
    }

    $returnhash->{rev}           = $r;
    $returnhash->{author}        = $author;
    $returnhash->{date}          = $date;
    $returnhash->{branch}        = $branch;
    $returnhash->{parent_branch} = $parent_branch;
    $returnhash->{parent_rev}    = $parent_rev;
    $returnhash->{dead_branch}   = $dead_branch;
    $returnhash->{merged}        = $merged;
    return $returnhash;

}

#
# --------------------------------------------------------------------
# --------------------------------------------------------------------
sub mk_tables
{
    my ($dbfile) = @_;
    return 1 if ($dbfile eq '');

    mk_nodes($dbfile);
    mk_branches($dbfile);
    mk_merges($dbfile);
    mk_counts($dbfile);
}

#
# --------------------------------------------------------------------
# --------------------------------------------------------------------
sub mk_counts
{
    my ($dbfile) = @_;
    return 1 if ($dbfile eq '');
    my $cmd;
    my $result;
    $cmd = "$sqlite $dbfile '.tables counts'";
    $result = `$cmd`; chomp $result;
    return if ($result eq 'counts');
    $cmd = 'CREATE TABLE counts '
                     . '('
                     .   'svn_hi_rev INTEGER, '
                     .   'svn_lo_rev INTEGER, '
                     .   'nodes_hi_rev INTEGER, '
                     .   'nodes_lo_rev INTEGER, '
                     .   'branches_hi_rev INTEGER, '
                     .   'branches_lo_rev INTEGER, '
                     .   'merges_hi_rev INTEGER, '
                     .   'merges_lo_rev INTEGER'
                     . ');';
    $cmd = "$sqlite $dbfile '$cmd'";
    system $cmd;

    $cmd = 'insert into counts '
                     . '('
                     .    'svn_hi_rev,'
                     .    'svn_lo_rev,'
                     .    'nodes_hi_rev,'
                     .    'nodes_lo_rev,'
                     .    'branches_hi_rev,'
                     .    'branches_lo_rev,'
                     .    'merges_hi_rev,'
                     .    'merges_lo_rev'
                     . ')'
                     . 'values (-1,-1,-1,-1,-1,-1,-1,-1);'
                     ;
    $cmd = "$sqlite $dbfile '$cmd'";
    system $cmd;
}


#
# --------------------------------------------------------------------
# --------------------------------------------------------------------
sub mk_nodes
{
    my ($dbfile) = @_;
    return 1 if ($dbfile eq '');
    my $cmd;
    my $result;
    $cmd = "$sqlite $dbfile '.tables nodes'";
    $result = `$cmd`; chomp $result;
    return if ($result eq 'nodes');
    $cmd = 'CREATE TABLE nodes '
                        . '('
                        .   'rev INTEGER PRIMARY KEY NOT NULL, '
                        .   'author TEXT, '
                        .   'date TEXT, '
                        .   'branch TEXT, '
                        .   'dead_branch TEXT'
                        . ");\n" ;
    $cmd = "$sqlite $dbfile '$cmd'";
    system $cmd;
}

#
# --------------------------------------------------------------------
# --------------------------------------------------------------------
sub mk_branches
{
    my ($dbfile) = @_;
    return 1 if ($dbfile eq '');
    my $cmd = "$sqlite $dbfile '.tables branches'";
    my $result = `$cmd`; chomp $result;
    return if ($result eq 'branches');
    my $tablecmd = "CREATE TABLE branches ("
                            . "branch_name TEXT, "
                            . "creation_rev INTEGER PRIMARY KEY NOT NULL, "
                            . "parent_branch_name TEXT, "
                            . "parent_branch_rev INTEGER"
                            . ");\n",
    open SQL, "|$sqlite $dbfile" or die;
    print SQL $tablecmd;
    close SQL;
}

#
# --------------------------------------------------------------------
# --------------------------------------------------------------------
sub mk_merges
{
    my ($dbfile) = @_;
    return 1 if ($dbfile eq '');
    my $cmd = "$sqlite $dbfile '.tables merges'";
    my $result = `$cmd`; chomp $result;
    return if ($result eq 'merges');
    my $tablecmd = 'CREATE TABLE merges ('
                            . 'merge_id INTEGER PRIMARY KEY AUTOINCREMENT, '
                            . 'from_rev INTEGER, '
                            . 'to_rev INTEGER '
                            . ");\n",
    open SQL, "|$sqlite $dbfile" or die;
    print SQL $tablecmd;
    close SQL;
}

#
# --------------------------------------------------------------------
# --------------------------------------------------------------------
sub get_counts
{
    my ($db, @columns) = @_;
    my $columns = join ',', @columns;
    my $cmd = "select $columns from counts limit 1;";
    $cmd = "$sqlite $db '$cmd'";
    my $result = `$cmd`;
    chomp $result;
    return split /\|/, $result;
}

#
# --------------------------------------------------------------------
# --------------------------------------------------------------------
sub set_counts
{
    my ($db, $column, $value) = @_;
    my $cmd = "UPDATE counts SET $column = $value";
    $cmd = "$sqlite $db '$cmd'";
    system $cmd;
}


1;
